/**
 * This file contains the code of an event recognizer.  It contains 2
 * classes: EventRecognizer and Connection.  
 */ 
package mac.eventRecognizer.interpreter;

import java.util.*;
import java.io.*;
import java.net.*;

import mac.filter.*;
import mac.eventRecognizer.pedlParser.*;
import mac.types.interfacePedlER.*;
import mac.types.parserTypes.*;

/** This class is an event recognizer.  It is an executable class.
 * <code>EventRecognizer</code> works as a multi-threaded server, i.e it
 * handles multiple connection requests from multiple filters.  It listens
 * connection requests from filters through socket network interface.  When
 * the connection is established, it creates <code>Connection</code> thread
 * which handles communication between a filter and an event recognizer.
 * <br>
 *
 * USAGE: <code>java mac.eventRecognizer.EventRecognizer listen_port
 * checker_host:checker_port parse_tree</code>
 *
 * ex> <code>java  mac.eventRecognizer.interpreter.EventRecognizer 8040
 * saul.cis.upenn.edu:8041 rrc.parse_tree</code> 
 *
 * @version Jan 29, 1999 
 * @author Moonjoo Kim
 */
public class EventRecognizer extends Thread {
    public final static int DEFAULT_INPUT_PORT = 8040;
    public final static int DEFAULT_OUTPUT_PORT = 8041;
    public String checker;	// Location of a checker as host:port format
    protected int port;		// Listening port to connection requests 
				// from filters 
    protected ServerSocket listen_socket;
    private FileInputStream fis ;	// File stream for monitoring script
    private ObjectInputStream ois;	// Object stream for

    public static ParserErObject input;
    public static ASTStart parseTree;
    public static Hashtable symbolTbl; 
    public static Vector exportEvents;
    public static Vector exportConditions;

    public static void fail(Exception e, String msg) {
	System.err.println(msg + ":" + e);
	System.exit(1);
    }

    public EventRecognizer(int port, String checker, String monscr_file){
	this.checker = checker;

	if(port == 0) port = DEFAULT_INPUT_PORT;
	this.port = port;
	try {	
		listen_socket = new ServerSocket(port);
	}
	catch(IOException e) {fail(e,"Exception creating EventRecognizer socket");}
	System.out.println("EventRecognizer:listening on port " + port);

	try{
	    fis = new FileInputStream(monscr_file);  // We use default name "t.out"
	    ois = new ObjectInputStream(fis);
	    input = (ParserErObject)ois.readObject();
	    parseTree = input.getParseTree();
	    symbolTbl = input.getSymbolTable();
	    System.err.println(symbolTbl);
	    //System.err.println("IC definition node:" + 
	    //((SymtabEntry)symbolTbl.get("IC")).getValue());
	    // parseTree.DFS();

	    

	    exportEvents = input.getEvents();
	    exportConditions= input.getConditions();

	    //input.dump("");
	} catch(IOException e) {
	    System.err.println(e);
	} catch( ClassNotFoundException e) {
	    System.err.println(e);
	} finally {
	    try{
		ois.close();
		fis.close();
	    } catch(IOException e) {
		System.err.println(e);
	    }
	}
	this.start();
    }


    /**
     * This method is listening a connection request from a filter.  Once it
     * receives a connection request, it creates a <code>Connection</code>
     * thread. 
     */
    public void run() {
	try {
	    while(true) {
		Socket client_socket = listen_socket.accept();
		Connection c = new Connection(client_socket,checker);
	    }
	}
	catch(IOException e) {
	    fail(e, "Exception while listening for connections");
	}
    }
    /** 
     * arg[0] : listening port to a filter
     * arg[1] : Checker location as a form of "host name:port#"
     * arg[2] : filename of parse tree object which is 
     *          generated monitoring script
     */
    public static void main(String[] args) {
	String checker= null;
	int port = 0;
	if(args.length == 3) {
	    try {
		    port = (Integer.valueOf(args[0])).intValue();
		    checker= args[1];
		    new EventRecognizer(port,checker,args[2]);
	    }
	    catch(NumberFormatException e){port = 0;}
	}
	else {
	    System.err.println("USAGE: " +
	    "java mac.eventRecognizer.EventRecognizer listen_port" +
	    " checker_host:checker_port parse_tree "); 
 
	    System.err.println("ex>"); 
	    System.err.println("java mac.eventRecognizer.EventRecognizer" +
		    " 8040 saul:8041 rrc.parse_tree");  
	    System.exit(1);
	}
    }
}


/**
 * This class 
 */
class Connection extends Thread {
    protected Socket client;
    Socket checker;
    protected DataInputStream in;
    protected ObjectInputStream o_in;
    PrintWriter out;
    static final String CHECKER_HOST = "saul.cis.upenn.edu";
    static final int CHECKER_PORT = 8041;

    /*
    int train_length ;
    int cross_x ;
    int cross_length ;
    float train_x;
    boolean IC ;
    */

    public Connection(Socket client_socket, String checkername) {
	client = client_socket;
	//IC = false;
	String checkerHost = checkername.substring(0,
		checkername.lastIndexOf(":"));
	int checkerPort = (Integer.valueOf(
		checkername.substring(checkername.lastIndexOf(":")+1, 
		checkername.length()))).intValue();
	try {
	    in = new DataInputStream(client.getInputStream());
	    o_in = new ObjectInputStream(in);
	    checker = new Socket(checkerHost,checkerPort);
	    out = new PrintWriter(checker.getOutputStream());
	    if(out != null) {
		System.err.println("Connection to RRCChecker established at " +
			checkerHost + ":" + checkerPort);
	    }
	} catch(IOException e) {
	    try{client.close();} catch(IOException e2){}
	    System.err.println(
		    "Exception while getting socket streams: " +e);
		return;
	}
	this.start();
    }

    private boolean isSymbolTblInitialized() {
	Enumeration elements = EventRecognizer.symbolTbl.elements();
	SymtabEntry se = null;

	while(elements.hasMoreElements()) {
	    se = (SymtabEntry)elements.nextElement();

	    // Be careful not to check whether an exec point is initialize.   
	    // An exec point does not have to be initialized
	    if(se.getValue() == null && !se.getType().equals("void")) {
		return false;
	    }
	}
	return true;
    }

    public void run() {
	String line;
	StringBuffer revline;
	int len;
	SnapshotObj s_o;

	try {
	    for(;;) {
		s_o = (SnapshotObj)o_in.readObject(); 
		System.out.println(s_o);
		resetSymbolTBL();	// reset updated flag. 
		updateSymbolTBL(s_o);
		if( isSymbolTblInitialized()) {
		    EventRecognizer.parseTree.resetMark();
		    //System.err.println("-----------------------");
		    EventRecognizer.parseTree.evaluate();
		    sendSignals(); 
		}
	    }
	}
	catch(EOFException e){}
	catch(IOException e){System.err.println(e);}
	catch(ClassNotFoundException e){System.err.println(e);}
	finally{ try {client.close();} catch(IOException e2){}}
    }

    void resetSymbolTBL() {
	Enumeration entries = EventRecognizer.symbolTbl.elements();
	while( entries.hasMoreElements()) {
	    ((SymtabEntry)(entries.nextElement())).resetUpdated();
	}
    }

    /**
     * Given snapshot object from the filter, it checks what events
     * happen or what conditions are satisfied.  Then it sends detected 
     * events and conditions to the run-time checker
     */
    void updateSymbolTBL(SnapshotObj s_o) {
	String varName = s_o.id;
	String execPoint = null;
	Object value = s_o.o;
	String methodName = null;
	SymtabEntry se = null;
	
	if(varName.equals("ExecPoint")) {	// Execpoint arrived
	    execPoint = (String)value;
	    methodName = execPoint.substring(execPoint.indexOf('f')+2);
	    
	    se  = (SymtabEntry)EventRecognizer.symbolTbl.get(methodName);
	    se.setOldValue(se.getValue());	// oldValue <- value	
	    se.setValue(execPoint.substring(0,execPoint.indexOf(' ')));
	    se.setUpdated();
	} else {	// Variable arrived

	    varName = varName.substring(varName.indexOf(' ') +1);
	    se = (SymtabEntry)EventRecognizer.symbolTbl.get(varName);
	    se.setOldValue(se.getValue());	// oldValue <- value	
	    se.setValue(value);
	    se.setUpdated();
	}
    }

    void sendSignals() {
	String eventName = null;
	SymtabEntry e = null;
	SimpleNode eventDefNode = null;

	for(int i=0; i < EventRecognizer.exportEvents.size(); i++) {
	    eventName = (String)EventRecognizer.exportEvents.elementAt(i);
	    e = (SymtabEntry)EventRecognizer.symbolTbl.get(eventName);
	    eventDefNode = (SimpleNode)(e.getValue());
	    //System.err.println("------------");
	    //System.err.println(eventDefNode);
	    if(((Boolean)(eventDefNode.getValue())).booleanValue()) {
		System.out.println(eventName + "happened!");
		out.println(eventName);
		out.flush();
	    }
	}
	// Please, fill up for exported conditions
    }
}
